package com.k8.rpc.util;

import com.k8.common.factroy.BeanFactory;
import com.k8.common.factroy.loader.ExtensionAccessor;
import com.k8.common.factroy.loader.ExtensionDirector;
import com.k8.common.factroy.loader.ExtensionLoader;
import com.k8.common.util.ClassUtil;
import com.k8.common.util.StrUtil;
import com.k8.rpc.RpcException;
import com.k8.rpc.cluster.Cluster;
import com.k8.rpc.cluster.ClusterBuilder;
import com.k8.rpc.cluster.loadBalance.LoadBalance;
import com.k8.rpc.exchange.Exchange;
import com.k8.rpc.exchange.ExchangeManager;
import com.k8.rpc.exchange.impl.DefaultExchangeManager;
import com.k8.rpc.expoter.Exporter;
import com.k8.rpc.invoke.InvokeMode;
import com.k8.rpc.invoke.Invoker;
import com.k8.rpc.invoke.InvokerBuilder;
import com.k8.rpc.invoke.invocation.Invocation;
import com.k8.rpc.invoke.invocation.RpcInvocation;
import com.k8.rpc.invoke.invocation.RpcInvocationTemplate;
import com.k8.rpc.invoke.invoker.DefaultInvokerBuilder;
import com.k8.rpc.registry.Registry;
import com.k8.rpc.registry.RegistryFactory;
import com.k8.rpc.serialize.Serializer;
import com.k8.rpc.url.ApiSignature;
import com.k8.rpc.url.RegistrySignature;
import com.k8.rpc.url.URL;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;

import static com.k8.common.constants.CommonConstants.MID_INTERVAL;
import static com.k8.common.constants.CommonKeyConstants.BALANCE_KEY;
import static com.k8.common.constants.CommonKeyConstants.SERIALIZE_KEY;

public class RpcUtils {
    public static <T> Invoker<T> buildConsumerInvoker(URL url, BeanFactory beanFactory, Class<T> type) {
        InvokerBuilder invokerBuilder = new DefaultInvokerBuilder();
        ExchangeManager exchangeManager = beanFactory.getOrRegistryBean(DefaultExchangeManager.class);
        Exchange exchange = exchangeManager.getOrRegistryExchange(URLUtils.getFullPath(url), url);
        return invokerBuilder.buildConsumer(type, url, exchange);
    }

    public static <T> Invoker<T> buildProviderInvoker(URL url, BeanFactory beanFactory, Class<T> type, T target) {
        InvokerBuilder invokerBuilder = new DefaultInvokerBuilder();
        return invokerBuilder.buildProvider(type, target, url, beanFactory);
    }

    public static <T> Map<String, RpcInvocationTemplate> resolveInvocationTemplates(Class<T> serviceType, Exporter<T> exporter, T instance) {
        Class<?> instanceClass = instance.getClass();
        Method[] apiMethods = serviceType.getDeclaredMethods();
        Map<String, RpcInvocationTemplate> result = null;
        try {
            result = new HashMap<>(apiMethods.length);
            for (Method apiMethod : apiMethods) {
                Class<?>[] parameterTypes = apiMethod.getParameterTypes();
                Method method = instanceClass.getMethod(apiMethod.getName(), parameterTypes);
                if (isValidRpcMethod(method)) {
                    String invocationKey = getInvocationKey(method, serviceType);
                    InvokeMode invokeMode = getInvokeMode(method.getReturnType());
                    RpcInvocationTemplate rpcInvocationTemplate = new RpcInvocationTemplate(method, invokeMode, invocationKey, exporter);
                    result.put(invocationKey, rpcInvocationTemplate);
                }
            }
        } catch (NoSuchMethodException e) {
            throw new RpcException(e.getMessage());
        }
        return result;
    }

    public static String getInvocationKey(Method method, Class<?> type) {
        return type.getTypeName() + MID_INTERVAL + ClassUtil.getMethodToString(method);
    }


    public static String getExporterSignature(ApiSignature apiSignature, RegistrySignature registrySignature) {
        return apiSignature.getSignature() + MID_INTERVAL + registrySignature.getSignature();
    }


    public static Invocation createConsumerInvocation(Method method, Object[] args, Class<?> type) {
        return createConsumerInvocation(method, args, null, type);
    }

    //调用方不再检验是否可序列化
    public static Invocation createConsumerInvocation(Method method, Object[] args, Map<String, Object> attachments, Class<?> type) {
        String invocationKey = RpcUtils.getInvocationKey(method, type);
        Class<?> returnType = method.getReturnType();
        InvokeMode invokeMode = getInvokeMode(returnType);
        return new RpcInvocation(method, args, attachments, invocationKey, invokeMode);
    }

    public static InvokeMode getInvokeMode(Class<?> clazz) {
        if (Future.class.equals(clazz)) {
            return InvokeMode.FUTURE;
        } else if (CompletableFuture.class.isAssignableFrom(clazz)) {
            return InvokeMode.ASYNC_FUTURE;
        } else {
            return InvokeMode.SYNC;
        }
    }

    /**
     * 判断方法是否符合RPC调用要求
     * 规则：
     * 1. 跳过Object类自带的方法（如toString()、hashCode()等）
     * 2. 参数不能是Future类型，且需符合序列化要求
     * 3. 返回值必须是Future类型或其子类
     *
     * @param method 需要检查的方法
     * @return 如果符合RPC调用要求则返回true，否则返回false
     */
    public static boolean isValidRpcMethod(Method method) {
        if (method == null) {
            return false;
        }

        // 跳过Object类自带的方法
        if (isObjectClassMethod(method)) {
            return false;
        }

        // 检查所有参数是否符合要求（不能是Future类型）
        Class<?>[] parameterTypes = method.getParameterTypes();
        for (Class<?> paramType : parameterTypes) {
            if (Future.class.isAssignableFrom(paramType)) {
                return false; // 参数不能是Future类型
            }
            if (!isValidRpcParameterType(paramType)) {
                return false;
            }
        }

        // 检查返回值必须是Future类型
        Class<?> returnType = method.getReturnType();
        if (!Future.class.isAssignableFrom(returnType)) {
            return false;
        }

        return true;
    }

    /**
     * 判断方法是否是Object类自带的方法
     */
    private static boolean isObjectClassMethod(Method method) {
        try {
            // 检查该方法是否在Object类中存在相同签名的方法
            Object.class.getMethod(method.getName(), method.getParameterTypes());
            return true;
        } catch (NoSuchMethodException e) {
            // 不是Object类的方法
            return false;
        }
    }

    /**
     * 判断参数类型是否符合RPC要求
     */
    private static boolean isValidRpcParameterType(Class<?> type) {
        // 检查是否为null
        if (type == null) {
            return false;
        }

        // 基本类型及其包装类都是可序列化的
        if (type.isPrimitive() || isWrapperType(type)) {
            return true;
        }

        // 检查是否可序列化
        if (isSerializable(type)) {
            return true;
        }

        // 检查是否为集合类型（如List等）
        if (Collection.class.isAssignableFrom(type)) {
            return true;
        }

        return false;
    }

    /**
     * 检查类型是否实现了Serializable接口
     */
    private static boolean isSerializable(Class<?> type) {
        return java.io.Serializable.class.isAssignableFrom(type);
    }

    /**
     * 检查是否为基本类型的包装类或String
     */
    private static boolean isWrapperType(Class<?> type) {
        return type == Integer.class || type == Long.class || type == Short.class ||
                type == Boolean.class || type == Byte.class || type == Character.class ||
                type == Float.class || type == Double.class || type == String.class;
    }


    public static Serializer getSerializer(URL url) {
        ExtensionAccessor extensionAccessor = url.getExtensionAccessor();
        ExtensionLoader<Serializer> serializerExtensionLoader = extensionAccessor.getExtensionLoader(Serializer.class);
        List<Serializer> serializers = serializerExtensionLoader.getExtensions();
        if (serializers.size() == 0) {
            throw new IllegalStateException("Serializer not found.");
        }
        Serializer serializer;
        String name = url.getParameter(SERIALIZE_KEY);
        if (StrUtil.isEmpty(name)) {
            serializer = serializers.get(0);
        } else {
            serializer = serializerExtensionLoader.getExtension(name);
            if (serializer == null) {
                serializer = serializers.get(0);
            }
        }
        return serializer;
    }

    public static String interfaceToPath(Class type, String group, String version) {
        return String.format("/%s_%s_%s", type.getName(), group == null ? "" : group,
                version == null ? "" : version);
    }


    public static Registry getRegistry(URL url, BeanFactory beanFactory) {
        RegistryFactory registryFactory = beanFactory.getExtensionDirector().getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
        return registryFactory.getRegistry(url);
    }

    public static Cluster getCluster(URL protocolUrl, BeanFactory beanFactory) {
        ExtensionDirector extensionDirector = beanFactory.getExtensionDirector();
        ExtensionLoader<LoadBalance> loadBalanceExtensionLoader = extensionDirector.getExtensionLoader(LoadBalance.class);
        LoadBalance loadBalance = loadBalanceExtensionLoader.getExtension(protocolUrl.getParameter(BALANCE_KEY));
        if (loadBalance == null) {
            loadBalance = loadBalanceExtensionLoader.getExtensions().get(0);
        }
        if (loadBalance == null) throw new IllegalStateException("The load balance is null.");
        ExtensionLoader<ClusterBuilder> clusterBuilderExtensionLoader = extensionDirector.getExtensionLoader(ClusterBuilder.class);
        ClusterBuilder clusterBuilder = clusterBuilderExtensionLoader.getExtension(protocolUrl.getParameter(BALANCE_KEY));
        if (clusterBuilder == null) {
            clusterBuilder = clusterBuilderExtensionLoader.getExtensions().get(0);
        }
        if (loadBalance == null) throw new IllegalStateException("The cluster builder is null.");
        return clusterBuilder.build(protocolUrl, loadBalance);
    }
}
